﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reactive.Linq;
using System.Threading;
using System.Reactive;
using System.ComponentModel.Composition;
using System.Reflection;
using Cofe.Core.Utils;
using Cofe.Core.Interfaces;
using Cofe.Core.Security;


namespace Cofe.Core.Logger
{
    [Export(typeof(ICofeService))]
    [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
    public class EventHub : IEventHub, INotifyImported
    {


        #region EventHolder static class

        internal static class EventHolder<T> where T : EventArgs
        {
            internal static event EventHandler<T> onEvent;
            internal static IObservable<IList<IEventLog<T>>> _listenEvents;

            static EventHolder()
            {
                _listenEvents = Observable.FromEventPattern<EventHandler<T>, T>(
                     h => onEvent += h, h => onEvent -= h)
                     .Distinct(e => e.EventArgs)
                     .Select(e => (IEventLog<T>)EventLog<T>.CreateEvent(e.Sender == null ? "" : e.Sender.ToString(), e.EventArgs))
                     .Buffer(CofeServices.CofeSettings.EventBufferTime);
            }

            internal static void RegisterEventSource<T>(IEventSource<T> eventSource) where T : EventArgs
            {
                eventSource.OnEvent += EventHolder<T>.DispatchEvent;
            }
            internal static void UnregisterEventSource<T>(IEventSource<T> eventSource) where T : EventArgs
            {
                eventSource.OnEvent -= EventHolder<T>.DispatchEvent;
            }

            internal static void DispatchEvent(object sender, T args)
            {
                if (onEvent != null)
                    onEvent(sender, args);
            }
        }
        #endregion

        #region Constructor

        public EventHub()
        {
            RegisterEventSource(ErrorEventSource.Instance);
        }

        #endregion

        #region Methods

        public void OnImported(Service.IServiceLocater serviceLocater)
        {
            serviceLocater.FindService<ISecurityManager>().ServicePermissionPolicies
                .Add(VolumePermissionPolicy.ForServices(null, PermissionType.All));
        }

        public IEventListener<T> SpawnListener<T>(Action<IEnumerable<IEventLog<T>>> handleAction, CancellationToken token) where T : EventArgs
        {
            IEventListener<T> retVal = new EventListener<T>(handleAction, token);
            RegisterEventListener<T>(retVal);
            return retVal;
        }

        public void RegisterEventListener(IEventListener listener)
        {
            EventHolder<EventArgs>._listenEvents.Subscribe(
                (elist) => listener.DispatchEvent(elist),
                listener.CancellationToken);
        }

        public void RegisterEventListener<T>(IEventListener<T> listener) where T : EventArgs
        {            
            EventHolder<T>._listenEvents.Subscribe(
                (eList) => listener.DispatchEvent(eList),
                listener.CancellationToken);
        }


        public void RegisterEventSource<T>(IEventSource<T> source) where T : EventArgs
        {
            Observable.FromEventPattern<EventHandler<T>, T>(
                h => source.OnEvent += h, h => source.OnEvent -= h)
                .Subscribe((ep) => DispatchEvent<T>(ep.Sender, ep.EventArgs), source.CancellationToken);
        }

        public void RegisterEventSource(IEventSource source)
        {

            MethodInfo mi = typeof(EventHub).GetMethods()
                .First(m => m.Name == "RegisterEventSource" && m.IsGenericMethod);

            foreach (var type in source.SupportedEventType)
            {
                mi.MakeGenericMethod(new Type[] { type }).Invoke(this, new object[] { source });
            }
        }

        public void DispatchEvent<T>(object sender, T args) where T : EventArgs
        {
            checkEventListenerFactoriesLoaded();
            EventHolder<T>.DispatchEvent(sender, args);
            if (typeof(T) != typeof(EventArgs))
                EventHolder<EventArgs>.DispatchEvent(sender, args);
        }

        private void checkEventListenerFactoriesLoaded()
        {
            lock (_eventListenerFactoryLock)
                if (!_loadedFactories)
                {
                    foreach (var f in EventListenerFactories)
                        f.RegisterEventListener(this);
                    _loadedFactories = true;
                }
        }

        #endregion

        #region Data

        private object _eventListenerFactoryLock = new object();
        private bool _loadedFactories = false;
        private IEventListenerFactory[] _eventListenerFactories = null;

        #endregion

        #region Public Properties

        public IEventSource<ErrorEventArgs> ErrorSource { get { return ErrorEventSource.Instance; } }
        public IEventListenerFactory[] EventListenerFactories
        {
            get { return _eventListenerFactories ?? CofeServices.ServiceLocater.FindAllServices<IEventListenerFactory>().ToArray(); }
            set { _eventListenerFactories = value; _loadedFactories = false; }
        }

        #endregion

    }

}
